package com.campuscarbooking.api.aspect;

import com.campuscarbooking.api.annotation.LogExecutionTime;
import com.campuscarbooking.api.annotation.LogMethod;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 日志切面，用于记录方法执行情况
 */
@Aspect
@Component
@Slf4j
public class LoggingAspect {

    /**
     * 定义切点 - 所有使用@LogExecutionTime注解的方法
     */
    @Pointcut("@annotation(com.campuscarbooking.api.annotation.LogExecutionTime)")
    public void logExecutionTimePointcut() {
    }

    /**
     * 定义切点 - 所有使用@LogMethod注解的方法
     */
    @Pointcut("@annotation(com.campuscarbooking.api.annotation.LogMethod)")
    public void logMethodPointcut() {
    }

    /**
     * 定义切点 - 所有控制器方法
     */
    @Pointcut("execution(* com.campuscarbooking.api.controller.*.*(..))")
    public void controllerPointcut() {
    }

    /**
     * 定义切点 - 所有服务方法
     */
    @Pointcut("execution(* com.campuscarbooking.api.service.*.*(..))")
    public void servicePointcut() {
    }

    /**
     * 环绕通知 - 记录方法执行时间
     */
    @Around("logExecutionTimePointcut()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        Object result = joinPoint.proceed();
        
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogExecutionTime annotation = method.getAnnotation(LogExecutionTime.class);
        String methodDesc = annotation.value().isEmpty() ? method.getName() : annotation.value();
        
        log.info("方法 [{}] 执行时间: {} ms", methodDesc, executionTime);
        
        return result;
    }

    /**
     * 前置通知 - 记录方法开始执行
     */
    @Before("logMethodPointcut()")
    public void logBefore(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogMethod annotation = method.getAnnotation(LogMethod.class);
        
        String methodDesc = annotation.value().isEmpty() ? method.getName() : annotation.value();
        log.info("开始执行方法 [{}]", methodDesc);
        
        if (annotation.logParams()) {
            log.info("方法 [{}] 参数: {}", methodDesc, Arrays.toString(joinPoint.getArgs()));
        }
    }

    /**
     * 返回通知 - 记录方法返回值
     */
    @AfterReturning(pointcut = "logMethodPointcut()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogMethod annotation = method.getAnnotation(LogMethod.class);
        
        String methodDesc = annotation.value().isEmpty() ? method.getName() : annotation.value();
        log.info("方法 [{}] 执行完成", methodDesc);
        
        if (annotation.logReturn()) {
            log.info("方法 [{}] 返回值: {}", methodDesc, result);
        }
    }

    /**
     * 异常通知 - 记录方法抛出的异常
     */
    @AfterThrowing(pointcut = "logMethodPointcut()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogMethod annotation = method.getAnnotation(LogMethod.class);
        
        String methodDesc = annotation.value().isEmpty() ? method.getName() : annotation.value();
        log.error("方法 [{}] 执行异常: {}", methodDesc, ex.getMessage(), ex);
    }

    /**
     * 环绕通知 - 记录控制器方法执行情况
     */
    @Around("controllerPointcut()")
    public Object logController(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = signature.getName();
        
        log.info("开始执行控制器方法: {}.{}", className, methodName);
        
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - startTime;
        
        log.info("控制器方法: {}.{} 执行完成, 耗时: {} ms", className, methodName, executionTime);
        
        return result;
    }
} 